﻿//----------------------------------------------------------------
// Copyright (c) 2011-2013 
// All rights reserved.
// Author: Ben DaoWei 
// Contact: bendaowei@gmail.com
// Web Link: http://xnaeditor.codeplex.com
// ----------------------------------------------------------------

namespace UnityEngine.Graphics
{
    using MdxLib.Model;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using System.Collections.Generic;
    using System;
    using System.Diagnostics;
    using UnityEngine.Graphics.Shader;
    using UnityEngine.Terrain;

    public class GeosetRenderer : GraphicsRenderer, IDisposable
    {
        private int[] _MapToBones;
        private bool[] _UsedForBones;

        public GeosetRenderer(CGeoset geoset, GraphicsDevice device)
            : base(device)
        {
            this.MaterialRenderer = new MaterialRenderer(geoset.Material.Object, device);

            this.Geoset = geoset;

            this.TriangleCount = this.Geoset.Faces.Count;
            this.VerticesCount = this.Geoset.Vertices.Count;

            this.VertexBuffer = new DynamicVertexBuffer(this.GraphicsDevice,
                                typeof(VertexPositionNormalTextureFogBones),
                                this.Geoset.Vertices.Count,
                                BufferUsage.None);

            int MaxIndex = 200;
            this._MapToBones = new int[MaxIndex];
            this._UsedForBones = new bool[MaxIndex];
            Array.Clear(_UsedForBones, 0, _UsedForBones.Length);

            // Vertex buffer
            VertexPositionNormalTextureFogBones[] data = new VertexPositionNormalTextureFogBones[this.VerticesCount];
            for (int i = 0; i < data.Length; i++)
            {
                CGeosetVertex v = this.Geoset.Vertices[i];
                data[i].Position = new Vector3(v.Position.X, v.Position.Y, v.Position.Z);
                data[i].Normal = new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z);
                data[i].TextureCoordinate = new Vector2(v.TexturePosition.X, v.TexturePosition.Y);
                data[i].Fog = 1.0f;

                CGeosetGroup group = this.Geoset.Vertices[i].Group.Object;// this.Geoset.Groups[vertexGroupID];
                if (group != null)
                {
                    //if (group.Nodes.Count > 4)
                    //    throw new Exception("Nodes count should not be above 4.");
                    for (int groupIndex = 0; groupIndex < group.Nodes.Count; groupIndex++)
                    {
                        int nodeIndex = group.Nodes[groupIndex].Node.NodeId;
                        _UsedForBones[nodeIndex] = true;
                    }
                }// group
            }

            // find which index the bone respond to
            int index = 0;
            for (int ii = 0; ii < MaxIndex; ii++)
                if (_UsedForBones[ii] == true)
                {
                    _MapToBones[ii] = index;
                    index++;
                }
            // directly input the id of each bone
            for (int i = 0; i < data.Length; i++)
            {
                CGeosetGroup group = this.Geoset.Vertices[i].Group.Object;// this.Geoset.Groups[vertexGroupID];
                if (group != null)
                {
                    //if (group.Nodes.Count > 4)
                    //    throw new Exception("Nodes count should not be above 4.");
                    data[i].Index.X = -1;
                    data[i].Index.Y = -1;
                    data[i].Index.Z = -1;
                    data[i].Index.W = -1;
                    for (int groupIndex = 0; groupIndex < group.Nodes.Count; groupIndex++)
                    {
                        int nodeIndex = group.Nodes[groupIndex].Node.NodeId;
                        switch (groupIndex)
                        {
                            case 0:
                                data[i].Index.X = _MapToBones[nodeIndex];
                                break;
                            case 1:
                                data[i].Index.Y = _MapToBones[nodeIndex];
                                break;
                            case 2:
                                data[i].Index.Z = _MapToBones[nodeIndex];
                                break;
                            case 3:
                                data[i].Index.W = _MapToBones[nodeIndex];
                                break;
                            default:
                                //Debug.WriteLine("Nodes count should not be above 4.");
                                break;
                        }
                    }
                }
            }
            this.VertexBuffer.SetData(data);

            // calculate bounding box
            List<Vector3> points = new List<Vector3>();
            for (int i = 0; i < data.Length; i++)
                points.Add(data[i].Position);
            _BoundingBox = BoundingBox.CreateFromPoints(points.ToArray());

            // index buffer
            ushort[] indices = new ushort[TriangleCount * 3];
            for (int j = 0; j < TriangleCount * 3; j += 3)
            {
                indices[j] = (ushort)geoset.Faces[j / 3].Vertex1.ObjectId;
                indices[j + 1] = (ushort)geoset.Faces[j / 3].Vertex2.ObjectId;
                indices[j + 2] = (ushort)geoset.Faces[j / 3].Vertex3.ObjectId;
            }

            this.IndexBuffer = new IndexBuffer(this.GraphicsDevice, typeof(ushort), this.TriangleCount * 3, BufferUsage.None);
            this.IndexBuffer.SetData(indices);
            this.Alpha = 1.0f;
            this.UseBones = false;
        }

        ~GeosetRenderer()
        {
            this.Dispose(false);
        }

        public void Dispose()
        {
            this.Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.VertexBuffer != null)
                    this.VertexBuffer.Dispose();

                if (this.IndexBuffer != null)
                    this.IndexBuffer.Dispose();
            }

            if (_MapToBones != null)
                _MapToBones = null;

            if (_UsedForBones != null)
                _UsedForBones = null;

            if (NodeRenderers != null)
            {
                NodeRenderers.Clear();
                NodeRenderers = null;
            }

            if (MaterialRenderer != null)
                MaterialRenderer = null;

            if (Geoset != null)
                Geoset = null;
        }

        public List<NodeRenderer> NodeRenderers { get; set; }

        public MaterialRenderer MaterialRenderer { get; set; }

        public DynamicVertexBuffer VertexBuffer { get; set; }

        public IndexBuffer IndexBuffer { get; set; }

        public CGeoset Geoset { get; set; }

        public int TriangleCount { get; set; }

        public int VerticesCount { get; set; }

        public float Alpha { get; set; }

        private bool UseBones { get; set; }

        private enum EChoose : int
        {
            NoProper = 0,
            ChooseA = 1,
            ChooseB = 2,
            ChooseNone = 3,
        }

        private EChoose OnEdge(Vector3 PointA, Vector3 TestPoint, Vector3 PointB)
        {
            const float nearDis = 0.1f;
            EChoose result = EChoose.NoProper;
            if ((Math.Abs(PointA.X - PointB.X) < nearDis && Math.Abs(TestPoint.X - PointA.X) < nearDis) ||
                (Math.Abs(PointA.Y - PointB.Y) < nearDis && Math.Abs(TestPoint.Y - PointA.Y) < nearDis))
            {
                result = EChoose.ChooseNone;
                if (Math.Abs(TestPoint.Z - PointA.Z) < 64.0f) result = EChoose.ChooseA;
                if (Math.Abs(TestPoint.Z - PointB.Z) < 64.0f) result = EChoose.ChooseB;
            }
            return result;
        }

        public bool Intersects(Ray ray, Matrix world)
        {
            Matrix inverseTransform = Matrix.Invert(world);

            ray.Position = Vector3.Transform(ray.Position, inverseTransform);
            ray.Direction = Vector3.TransformNormal(ray.Direction, inverseTransform);

            if (this._BoundingBox.Intersects(ray) != null)
                return true;
            else
                return false;
        }

        public void UpdateCliff(Vector3[] Positions, bool IsCliffTrans, EDirection Direction, ESide side, Matrix World)
        {
            // Actually, this cpu calculating should be separated from geoset renderer, as a part of Cliff renderer, which can not be done now.
            // It will be imported into an .fx file, and this can be ported into the cliff renderer

            // Logics
            // 1. Get Original Six Points Of Cliffs
            // 2. Get Transformed Six Points Of Cliffs
            // 3. Put Original points to transformed points, other points on edges will be set height by choosing Z near point

            // Validate the correction
            Debug.Assert(Positions.Length == 6);

            // The code is dirty, and will be changed placed or written in the .fx files
            VertexPositionNormalTextureFogBones[] VerticesCopy = new VertexPositionNormalTextureFogBones[this.VerticesCount];
            for (int i = 0; i < VerticesCopy.Length; i++)
            {
                CGeosetVertex v = this.Geoset.Vertices[i];
                VerticesCopy[i].Position = new Vector3(v.Position.X, v.Position.Y, v.Position.Z);
                VerticesCopy[i].Normal = new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z);
                VerticesCopy[i].TextureCoordinate = new Vector2(v.TexturePosition.X, v.TexturePosition.Y);
            }

            int Left = -128;
            int Top = 128;
            int Right = 0;
            int Bottom = 0;

            if (IsCliffTrans)
            {
                switch (Direction)
                {
                    case EDirection.North:
                        Top = 256;
                        break;
                    case EDirection.South:
                        Top = 256;
                        break;
                    case EDirection.West:
                        Left = -256;
                        break;
                    case EDirection.East:
                        Left = -256;
                        break;
                }
            }

            Vector3 AA, BB, CC, DD, EE, FF;
            AA = BB = CC = DD = EE = FF = new Vector3();
            for (int vertexIndex = 0; vertexIndex < VerticesCopy.Length; vertexIndex++)
            {
                CGeosetVertex v = this.Geoset.Vertices[vertexIndex];
                Vector3 position = new Vector3(v.Position.X, v.Position.Y, v.Position.Z);

                // (Left, Top) ----- (Right, Top)
                //      |       T      |
                //      |L            R|
                //      |       B      |
                // (Left, Bottom)  ----- (Right, Bottom)
                var A = new Vector2(Left, Bottom);
                var B = new Vector2(Left, Top);
                var C = new Vector2(Right, Top);
                var D = new Vector2(Right, Bottom);
                Vector2 E = new Vector2();
                Vector2 F = new Vector2();

                if (IsCliffTrans)
                {
                    switch (Direction)
                    {
                        case EDirection.North:
                            E = new Vector2(Left, (Top + Bottom) / 2);
                            F = new Vector2(Right, (Top + Bottom) / 2);
                            break;
                        case EDirection.South:
                            E = new Vector2(Left, (Top + Bottom) / 2);
                            F = new Vector2(Right, (Top + Bottom) / 2);
                            break;
                        case EDirection.West:
                            E = new Vector2((Left + Right) / 2, Top);
                            F = new Vector2((Left + Right) / 2, Bottom);
                            break;
                        case EDirection.East:
                            E = new Vector2((Left + Right) / 2, Top);
                            F = new Vector2((Left + Right) / 2, Bottom);
                            break;
                    }
                }

                Vector2 testPoint = new Vector2(position.X, position.Y);
                bool b0 = Vector2.Distance(A, testPoint) < 0.1f;// position.X == Left && position.Y == Bottom;
                bool b1 = Vector2.Distance(B, testPoint) < 0.1f;// position.X == Left && position.Y == Top;
                bool b2 = Vector2.Distance(C, testPoint) < 0.1f; // position.X == Right && position.Y == Top;
                bool b3 = Vector2.Distance(D, testPoint) < 0.1f; // position.X == Right && position.Y == Bottom;
                bool b4 = false;
                bool b5 = false;
                if (IsCliffTrans)
                {
                    b4 = Vector2.Distance(E, testPoint) < 0.1f;
                    b5 = Vector2.Distance(F, testPoint) < 0.1f;
                }

                if (b0) { AA = position; }
                if (b1) { BB = position; }
                if (b2) { CC = position; }
                if (b3) { DD = position; }
                if (IsCliffTrans)
                {
                    if (b4) { EE = position; }
                    if (b5) { FF = position; }
                }
            }

            for (int vertexIndex = 0; vertexIndex < VerticesCopy.Length; vertexIndex++)
            {
                CGeosetVertex v = this.Geoset.Vertices[vertexIndex];
                Vector3 position = new Vector3(v.Position.X, v.Position.Y, v.Position.Z);
                Vector3 final = Vector3.Transform(position, World);

                bool b0, b1, b2, b3;
                b0 = b1 = b2 = b3 = false;

                const float cmpEdge = 0.1f;
                const float cmp = 64.0f; ;

                // 0 is get around, 1 is lerp
                int strategy = 0;
                char edge = '0'; // L is L, T is T, R is R, B is B

                if (Math.Abs(position.X - Left) < cmpEdge) edge = 'L';
                if (Math.Abs(position.X - Right) < cmpEdge) edge = 'R';
                if (Math.Abs(position.Y - Bottom) < cmpEdge) edge = 'B';
                if (Math.Abs(position.Y - Top) < cmpEdge) edge = 'T';

                if (IsCliffTrans)
                {
                    switch (Direction)
                    {
                        case EDirection.North:
                            if (edge == 'L' && side == ESide.Left) { strategy = 1; }
                            if (edge == 'R' && side == ESide.Right) { strategy = 1; }
                            break;
                        case EDirection.South:
                            if (edge == 'R' && side == ESide.Left) { strategy = 1; }
                            if (edge == 'L' && side == ESide.Right) { strategy = 1; }
                            break;
                        case EDirection.West:
                            if (edge == 'B' && side == ESide.Left) { strategy = 1; }
                            if (edge == 'T' && side == ESide.Right) { strategy = 1; }
                            Left = -256;
                            break;
                        case EDirection.East:
                            if (edge == 'T' && side == ESide.Left) { strategy = 1; }
                            if (edge == 'B' && side == ESide.Right) { strategy = 1; }
                            Left = -256;
                            break;
                    }
                }

                if (strategy == 0)
                {
                    if (IsCliffTrans == false)
                    {
                        //  BB -- CC   1 -- 2
                        //   |     |   |    |
                        //  AA -- DD   0 -- 3
                        EChoose choose;
                        choose = OnEdge(AA, position, BB);
                        if (choose == EChoose.ChooseA) final.Y = Positions[0].Y;
                        if (choose == EChoose.ChooseB) final.Y = Positions[1].Y;
                        if (choose == EChoose.ChooseNone) final = Vector3.Lerp(Positions[0], Positions[1], 0.5f);

                        choose = OnEdge(BB, position, CC);
                        if (choose == EChoose.ChooseA) final.Y = Positions[1].Y;
                        if (choose == EChoose.ChooseB) final.Y = Positions[2].Y;
                        if (choose == EChoose.ChooseNone) final = Vector3.Lerp(Positions[1], Positions[2], 0.5f);

                        choose = OnEdge(CC, position, DD);
                        if (choose == EChoose.ChooseA) final.Y = Positions[2].Y;
                        if (choose == EChoose.ChooseB) final.Y = Positions[3].Y;
                        if (choose == EChoose.ChooseNone) final = Vector3.Lerp(Positions[2], Positions[3], 0.5f);

                        choose = OnEdge(DD, position, AA);
                        if (choose == EChoose.ChooseA) final.Y = Positions[3].Y;
                        if (choose == EChoose.ChooseB) final.Y = Positions[0].Y;
                        if (choose == EChoose.ChooseNone) final = Vector3.Lerp(Positions[3], Positions[0], 0.5f);
                    }
                    else
                    {
                        if (Direction == EDirection.West || Direction == EDirection.East)
                        {
                            //  BB -- EE -- CC   1 -- 4 -- 2
                            //   |           |   |         |
                            //  AA -- FF -- DD   0 -- 5 -- 3
                            EChoose choose;
                            choose = OnEdge(AA, position, BB);
                            if (choose == EChoose.ChooseA) final.Y = Positions[0].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[1].Y;

                            choose = OnEdge(BB, position, EE);
                            if (choose == EChoose.ChooseA) final.Y = Positions[1].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[4].Y;

                            choose = OnEdge(EE, position, CC);
                            if (choose == EChoose.ChooseA) final.Y = Positions[4].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[2].Y;

                            choose = OnEdge(CC, position, DD);
                            if (choose == EChoose.ChooseA) final.Y = Positions[2].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[3].Y;

                            choose = OnEdge(DD, position, FF);
                            if (choose == EChoose.ChooseA) final.Y = Positions[3].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[5].Y;

                            choose = OnEdge(FF, position, AA);
                            if (choose == EChoose.ChooseA) final.Y = Positions[5].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[0].Y;
                        }
                        else
                        {
                            //  BB -- CC  1 -- 2
                            //   |     |  |    |
                            //  EE    FF  4    5
                            //   |     |  |    |
                            //  AA -- DD  0 -- 3
                            EChoose choose;
                            choose = OnEdge(AA, position, EE);
                            if (choose == EChoose.ChooseA) final.Y = Positions[0].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[4].Y;

                            choose = OnEdge(EE, position, BB);
                            if (choose == EChoose.ChooseA) final.Y = Positions[4].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[1].Y;

                            choose = OnEdge(BB, position, CC);
                            if (choose == EChoose.ChooseA) final.Y = Positions[1].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[2].Y;

                            choose = OnEdge(CC, position, FF);
                            if (choose == EChoose.ChooseA) final.Y = Positions[2].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[5].Y;

                            choose = OnEdge(FF, position, DD);
                            if (choose == EChoose.ChooseA) final.Y = Positions[5].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[3].Y;

                            choose = OnEdge(DD, position, AA);
                            if (choose == EChoose.ChooseA) final.Y = Positions[3].Y;
                            if (choose == EChoose.ChooseB) final.Y = Positions[0].Y;
                        }

                    }

                    if (IsCliffTrans == false && edge == '0')
                    {
                        float maxH = Math.Max(Math.Max(Positions[0].Y, Positions[1].Y), Math.Max(Positions[2].Y, Positions[3].Y));
                        float minH = Math.Min(Math.Min(Positions[0].Y, Positions[1].Y), Math.Min(Positions[2].Y, Positions[3].Y));
                        float halfH = (maxH + minH) / 2.0f;

                        float maxZ = Math.Max(Math.Max(AA.Z, BB.Z), Math.Max(CC.Z, DD.Z));
                        float minZ = Math.Min(Math.Min(AA.Z, BB.Z), Math.Min(CC.Z, DD.Z));
                        float halfZ = (maxZ + minZ) / 2.0f;
                        if (maxZ - position.Z < cmp)
                            final.Y = maxH;
                        else
                            if (position.Z - minZ < cmp)
                                final.Y = minH;
                            else
                                final.Y = halfH;
                    }

                    //if (edge != '0' && IsCliffTrans)
                    //    DebugPrimitiveRenderer.AddImmdiatePrimitive(final, Color.LightPink);
                }
                else
                {
                    float amount = 0;
                    switch (edge)
                    {
                        case 'L':
                            amount = (position.Y - Bottom) / (float)(Top - Bottom);
                            final = Vector3.Lerp(Positions[0], Positions[1], amount);
                            break;
                        case 'R':
                            amount = (position.Y - Bottom) / (float)(Top - Bottom);
                            final = Vector3.Lerp(Positions[3], Positions[2], amount);
                            break;
                        case 'T':
                            amount = (position.X - Left) / (float)(Right - Left);
                            final = Vector3.Lerp(Positions[1], Positions[2], amount);
                            break;
                        case 'B':
                            amount = (position.X - Left) / (float)(Right - Left);
                            final = Vector3.Lerp(Positions[0], Positions[3], amount);
                            break;
                    }

                    //if (edge != '0')
                    //    DebugPrimitiveRenderer.AddImmdiatePrimitive(final, Color.LightPink);
                }


                if (position == AA) { final = Positions[0]; }
                if (position == BB) { final = Positions[1]; }
                if (position == CC) { final = Positions[2]; }
                if (position == DD) { final = Positions[3]; }
                if (IsCliffTrans)
                {
                    if (position == EE) { final = Positions[4]; }
                    if (position == FF) { final = Positions[5]; }
                }

                VerticesCopy[vertexIndex].Position = final;
                VerticesCopy[vertexIndex].Normal = Vector3.Transform(new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z), World);
                VerticesCopy[vertexIndex].Fog = 1.0f;
                VerticesCopy[vertexIndex].Index = Vector4.Zero;
            }

            this.VertexBuffer.SetData(VerticesCopy, 0, VerticesCopy.Length, SetDataOptions.Discard);
        }

        public bool IsTransparent()
        {
            bool isTransparent = false;
            for (int layerIndex = 0; layerIndex < this.MaterialRenderer.LayerRenderers.Length; layerIndex++)
            {
                MaterialLayerRenderer layer = this.MaterialRenderer.LayerRenderers[layerIndex];
                if (layer.CMaterialLayer.FilterMode != EMaterialLayerFilterMode.None &&
                    layer.CMaterialLayer.FilterMode != EMaterialLayerFilterMode.Transparent &&
                    layer.CMaterialLayer.FilterMode != EMaterialLayerFilterMode.Blend)
                    isTransparent = true;
            }
            return isTransparent;
        }

        public void Draw(Matrix world, Matrix view, Matrix projection)
        {
            if (this.Alpha < 0.01f)
                return;

            GraphicsDevice device = EffectManager.Instance.GraphicsDevice;

            device.SetVertexBuffer(this.VertexBuffer);

            device.Indices = this.IndexBuffer;

            for (int layerIndex = 0; layerIndex < this.MaterialRenderer.LayerRenderers.Length; layerIndex++)
            {
                MaterialLayerRenderer layer = this.MaterialRenderer.LayerRenderers[layerIndex];

                EffectManager.Instance.Shader.UseBones = this.UseBones;
                if (this.UseBones && this.NodeRenderers != null)
                {
                    // to list all bones that used
                    List<Matrix> matrix = new List<Matrix>();
                    for (int i = 0; i < this.NodeRenderers.Count; i++)
                        if (_UsedForBones[i])
                            matrix.Add(this.NodeRenderers[i].World);
                    EffectManager.Instance.Shader.Bones = matrix.ToArray();
                }
                EffectManager.Instance.Shader.FliterMode = layer.CMaterialLayer.FilterMode;
                EffectManager.Instance.Shader.World = world;
                EffectManager.Instance.Shader.View = view;
                EffectManager.Instance.Shader.Projection = projection;
                EffectManager.Instance.Shader.Alpha = this.Alpha;
                EffectManager.Instance.Shader.TextureEnabled = true;
                EffectManager.Instance.Shader.Texture = this.MaterialRenderer.LayerRenderers[layerIndex].TextureRenderer.Texture;

                // Loop over all the effect passes.
                foreach (EffectPass pass in EffectManager.Instance.Shader.Effect.CurrentTechnique.Passes)
                {
                    pass.Apply();

                    // Draw the geometry.
                    device.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                                 0, 0, this.VerticesCount,
                                                 0, this.TriangleCount);
                }
            }

            device.SetVertexBuffer(null);
            device.Indices = null;
        }

        private BoundingBox _BoundingBox;
        public BoundingBox BoundingBox
        {
            get
            {
                return _BoundingBox;
            }
        }

        public void Update()
        {
            this.UseBones = true;
            return;

            VertexPositionNormalTextureFogBones[] VerticesCopy = new VertexPositionNormalTextureFogBones[this.VerticesCount];
            for (int i = 0; i < VerticesCopy.Length; i++)
            {
                CGeosetVertex v = this.Geoset.Vertices[i];
                VerticesCopy[i].Position = new Vector3(v.Position.X, v.Position.Y, v.Position.Z);
                VerticesCopy[i].Normal = new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z);
                VerticesCopy[i].TextureCoordinate = new Vector2(v.TexturePosition.X, v.TexturePosition.Y);
                VerticesCopy[i].Fog = 1.0f;
                VerticesCopy[i].Index = Vector4.Zero;
            }

            // cpu calculation of vertex position
            for (int vertexIndex = 0; vertexIndex < this.Geoset.Vertices.Count; vertexIndex++)
            {
                CGeosetGroup group = this.Geoset.Vertices[vertexIndex].Group.Object;// this.Geoset.Groups[vertexGroupID];

                Matrix matrix = new Matrix();
                for (int groupIndex = 0; groupIndex < group.Nodes.Count; groupIndex++)
                {
                    int nodeIndex = group.Nodes[groupIndex].Node.NodeId;
                    matrix += this.NodeRenderers[nodeIndex].World / (float)group.Nodes.Count;
                }

                VerticesCopy[vertexIndex].Position = Vector3.Transform(VerticesCopy[vertexIndex].Position, matrix);
            }
            this.VertexBuffer.SetData(VerticesCopy, 0, VerticesCopy.Length, SetDataOptions.Discard);
        }
    }
}
